<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>Polar Clock</title>
		<script src="raphael.js"></script>
		<script>



var PolarClock = {};

PolarClock.Clock = function() {
	var self = this;

	var getClockWidth = function() {
		var numRings = 6; /*hour, minutes, seconds, day, month, weekday*/
		var clockWidth =
			2 * PolarClock.Options.InnerRingRadius +  /* The diameter of the innermost circle */
			2 * (numRings - 1) * PolarClock.Options.RingSpacing +  /* The spaces between all circles. */
			2 * numRings * PolarClock.Options.RingWidth - PolarClock.Options.RingWidth +  /* The width of all circle strokes */
			PolarClock.Options.RingWidth;  /* Extra padding (zero would put outermost circle against edge) */

		return clockWidth;
	}

	var getSizeInfo = function() {
		var minDim = document.documentElement.clientWidth < document.documentElement.clientHeight ? document.documentElement.clientWidth : document.documentElement.clientHeight;
		var paperHeight = document.documentElement.clientHeight - 4;
		var paperWidth = document.documentElement.clientWidth;
		var center = {x: paperWidth / 2, y: paperHeight / 2};
		var scale = minDim / getClockWidth();

		return {Scale : scale, Center : center, PaperWidth : paperWidth, PaperHeight: paperHeight};
	}

	var opts = PolarClock.Options;
	var sizes = getSizeInfo();
	var paper = Raphael("holder", sizes.PaperWidth, sizes.PaperHeight);
	//var d = new Date(2016, 11, 31, 23,59,55);

	var rings = [
		new PolarClock.Ring(paper, sizes.Center, sizes.Scale, opts.Interval.Seconds),
		new PolarClock.Ring(paper, sizes.Center, sizes.Scale, opts.Interval.Minutes),
		new PolarClock.Ring(paper, sizes.Center, sizes.Scale, opts.Interval.Hours),
		new PolarClock.Ring(paper, sizes.Center, sizes.Scale, opts.Interval.Months),
		new PolarClock.Ring(paper, sizes.Center, sizes.Scale, opts.Interval.Days),
		new PolarClock.Ring(paper, sizes.Center, sizes.Scale, opts.Interval.DaysOfWeek)
	];

	self.tick = function() {
		var d = new Date();
		//d.setSeconds(d.getSeconds() + 1);
		for (var i = 0; i < rings.length; ++i) {
			rings[i].tick(d);
		}
	}

	self.resize = function() {
		var sizes = getSizeInfo();

		paper.setSize(sizes.PaperWidth, sizes.PaperHeight);

		for (var i = 0; i < rings.length; ++i) {
			rings[i].resize(sizes.Center, sizes.Scale);
		}
	}
}

PolarClock.Ring = function (paper, center, scale, interval) {
	var self = this;

	var setSizes = function(center, scale) {
		var clockRadius = PolarClock.Options.InnerRingRadius + (PolarClock.Options.RingSpacing + PolarClock.Options.RingWidth) * interval.Index;
		track.attr({cx: center.x, cy: center.y, r: clockRadius * scale, "stroke-width":  PolarClock.Options.RingWidth * scale});
		ring.attr({"stroke-width":  PolarClock.Options.RingWidth * scale, path : track.getSubpath(0, 0) });
		_currentSegment = 0;
	}

	var track = paper.circle().attr({stroke: PolarClock.Options.GhostRingColor});
	var ring = paper.path();
	var _currentSegment = 0;
	var _currentDate;

	setSizes(center, scale);

	var add = function(val) {
		ring.animate({
			path :  track.getSubpath(0, val * track.getTotalLength() / interval.Segments(_currentDate.getTime())),
			stroke : "hsl(" + val / interval.Segments(_currentDate.getTime()) + ", 1, .5)"
		}, 75, "linear");

		_currentSegment = val;
	}

	var reset = function(val) {
		ring.animate({"path" :  track.getSubpath(0, track.getTotalLength() - .01)}, 75, "linear", function() {
			var rollback = function() {
				if (ring.getTotalLength() >= track.getTotalLength() / interval.Segments(_currentDate.getTime())) {
					ring.animate({
							path : track.getSubpath(0, ring.getTotalLength() / 1.4)
						}, 750 / interval.Segments(_currentDate.getTime()), "linear", rollback
					);
				} else {
					ring.animate({path : track.getSubpath(0, 0)}, 750 / interval.Segments(_currentDate.getTime()), "linear");
				}
			}
			rollback();
		});

		_currentSegment = val - 1;
	}

	self.tick = function(date) {
		_currentDate = date;

		var currentDatePart = _currentDate[interval.DateMethod]()
		var segmentsToShow = currentDatePart - interval.StartAt + 1;

		if (_currentSegment < segmentsToShow) {
			add(segmentsToShow);
		} else if (_currentSegment > segmentsToShow) {
			reset(currentDatePart);
		}
	}

	self.resize = function(center, scale) {
		setSizes(center, scale);
	}
}


PolarClock.Options = {
	InnerRingRadius : 40,
	RingSpacing : 10,
	RingWidth : 30,
	GhostRingColor : "#070707",
	getMonthName : function(index) { return ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][index]; },
	getDayOfWeekName : function(index) { return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][index]; },
	Interval : {
		Minutes : { Index : 4, DateMethod : "getMinutes", Segments : function(time) { return 60; }, StartAt : 1 },
		Seconds : { Index : 5, DateMethod : "getSeconds", Segments : function(time) { return 60; }, StartAt : 1 },
		Hours : { Index : 3, DateMethod : "getHours", Segments : function(time) { return 24; }, StartAt : 1 },
		Months : { Index : 1, DateMethod : "getMonth", Segments : function(time) { return 12; }, StartAt : 0 },
		Days : { Index : 2, DateMethod : "getDate", Segments :
			function(time) {
				var d = new Date(time);
                d.setDate(1);
				d.setMonth(d.getMonth() + 1);
				d.setDate(0);
				return d.getDate();
			}, StartAt : 1
		},
		DaysOfWeek : { Index : 0, DateMethod : "getDay", Segments : function(time) { return 7; }, StartAt : 0 }
	}
}



window.onload = function () {
	clock = new PolarClock.Clock();

	(function () {
		clock.tick();
		setTimeout(arguments.callee, 1000 - new Date().getMilliseconds());
	})();

	window.onresize = function() {
		clock.resize();
	}
};





		</script>
		<style media="screen">
			body {background:#000;margin:0;/*height:1280px;width:1024px;*/}
			#holder{height:100%;width:100%;}
		</style>
	</head>
	<body>
		<div id="holder"></div>
	</body>
</html>